package org.abacus.standard;

public final class Base64Coder {
	private static final char[] conversionTable = new char[] {

	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',

	'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',

	'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',

	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',

	'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',

	'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',

	'8', '9', '+', '/', '=' };

	private Base64Coder() {
		super();
	}

	public static final String encode(byte[] arg0) {
		return encode(arg0, false);
	}

	public static final String encode(byte[] arg0, boolean hasline) {
		if (null == arg0) {
			return null;
		}
		if (0 == arg0.length) {
			return "";
		}
		StringBuffer sb = new StringBuffer((arg0.length + 2) / 3 * 2);
		int mod = arg0.length % 3;
		if (mod != 0) {
			for (int i = 0; i < arg0.length - mod; i += 3) {
				if (hasline) {
					if (i != 0 && i % 57 == 0) {
						sb.append('\n');
					}
				}
				sb.append(conversionTable[((arg0[i] & 0XFC) >>> 2)]);
				sb.append(conversionTable[((arg0[i] & 0X03) << 4) + ((arg0[i + 1] & 0XF0) >>> 4)]);
				sb.append(conversionTable[((arg0[i + 1] & 0X0F) << 2) + ((arg0[i + 2] & 0XC0) >>> 6)]);
				sb.append(conversionTable[arg0[i + 2] & 0X3F]);
			}
			int i = arg0.length - mod;
			if (mod == 1) {
				if (hasline) {
					if (i != 0 && i % 57 == 0) {
						sb.append('\n');
					}
				}
				sb.append(conversionTable[((arg0[i] & 0XFC) >>> 2)]);
				sb.append(conversionTable[((arg0[i] & 0X03) << 4)]);
				sb.append(conversionTable[conversionTable.length - 1]);
				sb.append(conversionTable[conversionTable.length - 1]);
			} else if (mod == 2) {
				if (hasline) {
					if (i != 0 && i % 57 == 0) {
						sb.append('\n');
					}
				}
				sb.append(conversionTable[((arg0[i] & 0XFC) >>> 2)]);
				sb.append(conversionTable[((arg0[i] & 0X03) << 4) + ((arg0[i + 1] & 0XF0) >>> 4)]);
				sb.append(conversionTable[((arg0[i + 1] & 0X0F) << 2)]);
				sb.append(conversionTable[conversionTable.length - 1]);
			}
		} else {
			for (int i = 0; i < arg0.length; i += 3) {
				if (hasline) {
					if (i != 0 && i % 57 == 0) {
						sb.append('\n');
					}
				}
				sb.append(conversionTable[((arg0[i] & 0XFC) >>> 2)]);
				sb.append(conversionTable[((arg0[i] & 0X03) << 4) + ((arg0[i + 1] & 0XF0) >>> 4)]);
				sb.append(conversionTable[((arg0[i + 1] & 0X0F) << 2) + ((arg0[i + 2] & 0XC0) >>> 6)]);
				sb.append(conversionTable[arg0[i + 2] & 0X3F]);
			}
		}
		return sb.toString();
	}

	public static String decode(byte[] arg0) {
		if (null == arg0) {
			return null;
		}
		if (0 == arg0.length) {
			return "";
		}
		boolean hasline = false;
		if (arg0.length >= 77) {
			hasline = 0X0A == arg0[76];
		}
		byte[] data;
		int i = 0;
		if (hasline) {
			i = arg0.length / 77;
		}
		if ('=' == arg0[arg0.length - 1]) {
			if ('=' == arg0[arg0.length - 2]) {
				data = new byte[(arg0.length - i) / 4 * 3 - 2];
			} else {
				data = new byte[(arg0.length - i) / 4 * 3 - 1];
			}
		} else {
			data = new byte[(arg0.length - i) / 4 * 3];
		}
		int j = 0;
		for (i = 0; i < arg0.length;) {
			if (hasline) {
				if (arg0[i] == '\n') {
					i += 1;
					continue;
				}
			}
			data[j] = (byte) ((conversionTableIndex(arg0[i]) << 2) + (conversionTableIndex(arg0[i + 1]) >> 4));
			if ('=' != arg0[i + 2]) {
				data[j + 1] = (byte) ((conversionTableIndex(arg0[i + 1]) << 4) + (conversionTableIndex(arg0[i + 2]) >> 2));
				if ('=' != arg0[i + 3]) {
					data[j + 2] = (byte) ((conversionTableIndex(arg0[i + 2]) << 6) + (conversionTableIndex(arg0[i + 3])));
				}
			}
			i += 4;
			j += 3;
		}
		return new String(data);
	}

	private static int conversionTableIndex(byte arg0) {
		for (int i = 0; i < conversionTable.length - 1; i++) {
			if (arg0 == conversionTable[i]) {
				return i;
			}
		}
		throw new RuntimeException("invalid ciphertext.");
	}
}
